// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; indent-tabs-mode: nil; -*-

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void test(int n)
{
	int dst, src;
	int rank, size, i;
	MPI_Status status;
	MPI_Request *recv_request, *send_request;
	
	int *send_data = (int*) malloc(n*sizeof(int));
	int *recv_data = (int*) malloc(n*sizeof(int));
	
	if (!send_data || !recv_data)
	{
		fprintf(stderr, "Out of memory!\n");
		abort();
	}
	
	// rank is meant to be the rank of this process.
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	
	for(i = 0; i < n; ++i)
	{
		send_data[i] = rank;
	}
	
	// size is meant to be the size of the communicator.
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	
	
	// BLOCKING DEADLOCK SHITE
	/*for(i = 0; i < size; ++i)
	{
		
		dst = (rank + 1) % size;
		src = (rank - 1 + size) % size;
		
		//fprintf(stderr, "Sending shit\n");
		
		// Send & receive.
		MPI_Send(send_data, n, MPI_INT, dst, 42, MPI_COMM_WORLD);
		
		MPI_Recv(recv_data, n, MPI_INT, src, 42, MPI_COMM_WORLD, &status);
		
		// You'll need that at some point.
		memcpy(send_data, recv_data, n*sizeof(int));
	}*/
	
	// BLOCKING NODEADLOCK SHITE
	/*for(i = 0; i < size; ++i)
	{
		
		dst = (rank + 1) % size;
		src = (rank - 1 + size) % size;
		
		if (rank % 2 == 1)
		{
			// Send & receive.
			MPI_Send(send_data, n, MPI_INT, dst, 42, MPI_COMM_WORLD);
			
			MPI_Recv(recv_data, n, MPI_INT, src, 42, MPI_COMM_WORLD, &status);
		}
		else
		{
			// Send & receive.
			MPI_Recv(recv_data, n, MPI_INT, src, 42, MPI_COMM_WORLD, &status);
			
			MPI_Send(send_data, n, MPI_INT, dst, 42, MPI_COMM_WORLD);
		}
		
		// You'll need that at some point.
		memcpy(send_data, recv_data, n*sizeof(int));
	}*/
	
	recv_request = (MPI_Request*) malloc(size*sizeof(MPI_Request));
	send_request = (MPI_Request*) malloc(size*sizeof(MPI_Request));
	
	// NOBLOCKING NODEADLOCK SHITE
	for(i = 0; i < size; ++i)
	{
		
		dst = (rank + 1) % size;
		src = (rank - 1 + size) % size;
		
		//fprintf(stderr, "Sending shit\n");
		
		// Send & receive.
		MPI_Isend(send_data, n, MPI_INT, dst, 42, MPI_COMM_WORLD, &send_request[i]);
		
		MPI_Irecv(recv_data, n, MPI_INT, src, 42, MPI_COMM_WORLD, &recv_request[i]);
		
		MPI_Wait(&send_request[i], &status);
		MPI_Wait(&recv_request[i], &status);
		
		// You'll need that at some point.
		memcpy(send_data, recv_data, n*sizeof(int));
	}
	
}


int main(int argc, char *argv[])
{
	int n;
	
	
	
	if (argc < 2)
	{
		fprintf(stderr, "Usage: %s size\n", argv[0]);
		abort();
	}

	n = atoi(argv[1]);

	if (n < 1)
	{
		fprintf(stderr, "Minimum value is 1.\n");
		abort();
	}
	
	MPI_Init(&argc, &argv);
	
	test(n);
	
	MPI_Finalize();
	
	return 0;
}
